#include "Int_TD.h"
#include "esp_log.h"

#define CONFIG_EXAMPLE_PIN_CMD 48
#define CONFIG_EXAMPLE_PIN_CLK 47
#define CONFIG_EXAMPLE_PIN_D0 21

// 挂载点
#define MOUNT_POINT "/sdcard"
// 写入文件大小
#define EXAMPLE_MAX_CHAR_SIZE 64
static const char *TAG = "example";

/**
 * @brief 向指定路径的文件写入数据
 *
 * 此函数尝试以写入模式打开指定路径的文件，若文件成功打开，
 * 则将传入的数据写入该文件，最后关闭文件并返回操作结果。
 *
 * @param path 要写入数据的文件的路径
 * @param data 要写入文件的数据
 * @return esp_err_t 操作结果，ESP_OK 表示成功，ESP_FAIL 表示失败
 */
static esp_err_t s_example_write_file(const char *path, char *data) {
  // 记录信息日志，表明正在尝试打开指定路径的文件
  ESP_LOGI(TAG, "Opening file %s", path);
  // 以写入模式打开文件，若打开成功则返回文件指针，失败则返回 NULL
  FILE *f = fopen(path, "w");
  // 检查文件是否成功打开
  if (f == NULL) {
    // 若文件打开失败，记录错误日志
    ESP_LOGE(TAG, "文件打开失败");
    // 返回失败状态
    return ESP_FAIL;
  }
  // 将传入的数据写入打开的文件
  fprintf(f, data);
  // 关闭已打开的文件，释放相关资源
  fclose(f);
  // 记录信息日志，表明文件写入操作已完成
  ESP_LOGI(TAG, "文件写入操作已完成");

  // 返回成功状态
  return ESP_OK;
}

/**
 * @brief 从指定路径的文件中读取一行数据
 *
 * 该函数尝试以只读模式打开指定路径的文件，若文件打开成功，
 * 则读取文件的第一行数据，去除行末的换行符后将读取到的数据记录到日志中，
 * 最后关闭文件并返回操作结果。
 *
 * @param path 要读取数据的文件的路径
 * @return esp_err_t 操作结果，ESP_OK 表示成功，ESP_FAIL 表示失败
 */
static esp_err_t s_example_read_file(const char *path) {
  // 记录信息日志，提示正在尝试读取指定路径的文件
  ESP_LOGI(TAG, "Reading file %s", path);
  // 以只读模式打开指定路径的文件，若打开成功返回文件指针，失败则返回 NULL
  FILE *f = fopen(path, "r");
  // 检查文件是否成功打开
  if (f == NULL) {
    // 若文件打开失败，记录错误日志
    ESP_LOGE(TAG, "文件打开失败");
    // 返回失败状态
    return ESP_FAIL;
  }
  // 定义一个字符数组，用于存储从文件中读取的一行数据
  char line[EXAMPLE_MAX_CHAR_SIZE];
  // 从文件中读取一行数据，最多读取 sizeof(line) - 1 个字符
  fgets(line, sizeof(line), f);
  // 关闭已打开的文件，释放相关资源
  fclose(f);

  // 去除读取到的字符串末尾的换行符
  // 查找换行符 '\n' 在字符串中的位置
  char *pos = strchr(line, '\n');
  if (pos) {
    // 若找到换行符，则将其替换为字符串结束符 '\0'
    *pos = '\0';
  }
  // 记录信息日志，打印从文件中读取到的数据
  ESP_LOGI(TAG, "从文件中读取到的数据: '%s'", line);

  // 返回成功状态
  return ESP_OK;
}

/**
 * @brief 应用程序的主入口函数
 *
 * 此函数用于初始化 SD 卡，挂载文件系统，对 SD 卡上的文件进行读写、重命名等操作，
 * 最后卸载文件系统并禁用 SDMMC 外设。
 */
void Int_TD_Init(void) {
  // 用于存储操作的返回状态
  esp_err_t ret;

  // 配置 SD 卡文件系统挂载参数
  // .format_if_mount_failed = true 表示挂载失败时尝试格式化 SD 卡
  // .max_files = 5 表示允许同时打开的最大文件数为 5
  // .allocation_unit_size = 16 * 1024 表示分配单元大小为 16KB
  esp_vfs_fat_sdmmc_mount_config_t mount_config = {.format_if_mount_failed = true, .max_files = 5, .allocation_unit_size = 16 * 1024};
  // 指向 SD 卡对象的指针
  sdmmc_card_t *card;
  // 定义 SD 卡挂载点
  const char mount_point[] = MOUNT_POINT;
  // 记录信息日志，提示正在初始化 SD 卡
  ESP_LOGI(TAG, "Initializing SD card");

  // 记录信息日志，提示使用 SDMMC 外设
  ESP_LOGI(TAG, "Using SDMMC peripheral");

  // 初始化 SDMMC 主机配置为默认值
  sdmmc_host_t host = SDMMC_HOST_DEFAULT();

  // 初始化 SDMMC 插槽配置为默认值
  sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
  // 设置 SD 卡数据总线宽度为 1 位
  slot_config.width = 1;
  // 设置 SD 卡时钟线对应的引脚
  slot_config.clk = CONFIG_EXAMPLE_PIN_CLK;
  // 设置 SD 卡命令线对应的引脚
  slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD;
  // 设置 SD 卡数据 0 线对应的引脚
  slot_config.d0 = CONFIG_EXAMPLE_PIN_D0;

  // 启用 SDMMC 插槽的内部上拉电阻
  slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;

  // 记录信息日志，提示正在挂载文件系统
  ESP_LOGI(TAG, "Mounting filesystem");
  // 尝试将 SD 卡挂载到指定的挂载点
  ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);

  // 检查挂载操作是否失败
  if (ret != ESP_OK) {
    if (ret == ESP_FAIL) {
      // 若挂载失败，记录错误日志，提示可能需要格式化 SD 卡
      ESP_LOGE(TAG, "Failed to mount filesystem. "
                    "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
    } else {
      // 若初始化 SD 卡失败，记录错误日志，提示检查 SD 卡线路
      ESP_LOGE(TAG,
               "Failed to initialize the card (%s). "
               "Make sure SD card lines have pull-up resistors in place.",
               esp_err_to_name(ret));
    }
    // 挂载失败，退出函数
    return;
  }
  // 记录信息日志，提示文件系统已挂载
  ESP_LOGI(TAG, "Filesystem mounted");

  // 打印 SD 卡的信息到标准输出
  sdmmc_card_print_info(stdout, card);

  /////////////////////////////////////////////
  // 定义要写入的文件路径
  const char *file_hello = MOUNT_POINT "/hello.txt";
  // 定义存储要写入数据的缓冲区
  char data[EXAMPLE_MAX_CHAR_SIZE];
  // 格式化要写入的数据
  snprintf(data, EXAMPLE_MAX_CHAR_SIZE, "%s %s!\n", "Hello", card->cid.name);
  // 调用函数将数据写入文件
  ret = s_example_write_file(file_hello, data);
  // 检查写入操作是否失败，失败则退出函数
  if (ret != ESP_OK) {
    MY_LOGE("写入文件失败");
    return;
  }

  // 调用函数从文件中读取数据
  ret = s_example_read_file(file_hello);
  // 检查读取操作是否失败，失败则退出函数
  if (ret != ESP_OK) {
    MY_LOGE("读取文件失败");
    return;
  }

  // 所有操作完成，卸载 SD 卡分区并禁用 SDMMC 外设
  esp_vfs_fat_sdcard_unmount(mount_point, card);
  // 记录信息日志，提示 SD 卡已卸载
  ESP_LOGI(TAG, "Card unmounted");
}
